package com.shixulin.oss;

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.internal.Mimetypes;
import com.aliyun.oss.model.*;
import com.shixulin.entity.filess;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URL;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.*;


@Component
public class handleBigFile {
    @Value("${aliyun.oss.endpoint}")
    private String ALIYUN_OSS_ENDPOINT;
    @Value("${aliyun.oss.accessKeyId}")
    private String ALIYUN_OSS_ACCESSKEYID;
    @Value("${aliyun.oss.accessKeySecret}")
    private String ALIYUN_OSS_ACCESSKEYSECRET;
    @Value("${aliyun.oss.bucketName}")
    private String ALIYUN_OSS_BUCKETNAME;
    //域名
    private static final String DOMAIN = "http://shixulin.com";

    public filess uploadBigFile(MultipartFile multipartFile)  {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
// 获取文件名
        String fileName = multipartFile.getOriginalFilename();
// 获取文件后缀名
        assert fileName != null;
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
// 最后上传生成的文件名
        String finalFileName = System.currentTimeMillis() + "" + new SecureRandom().nextInt(0x0400) + suffixName;
// oss中的文件夹名
        String objectName = "studyFile/"+ sdf.format(new Date()) + "/" + finalFileName;
// 创建上传文件的元信息，可以通过文件元信息设置HTTP header(设置了才能通过返回的链接直接访问)。
//        ObjectMetadata objectMetadata = new ObjectMetadata();
//        objectMetadata.setContentType("image/jpg");

//        ObjectMetadata metadata = new ObjectMetadata();
//        metadata.setHeader("Content-Disposition", "inline");

        // 创建OSSClient实例。
        OSSClient ossClient = new OSSClient(ALIYUN_OSS_ENDPOINT, ALIYUN_OSS_ACCESSKEYID, ALIYUN_OSS_ACCESSKEYSECRET);
        try {
            // 创建InitiateMultipartUploadRequest对象。
            InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(ALIYUN_OSS_BUCKETNAME, objectName);


            // 根据文件自动设置ContentType。如果不设置，ContentType默认值为application/oct-srream。
//            if (metadata.getContentType() == null) {
//                metadata.setContentType(Mimetypes.getInstance().getMimetype(new File(String.valueOf(multipartFile)), objectName));
//            }
//            if (metadata.getContentType() == null) {
//                metadata.setContentType(multipartFile.getContentType());
//            }

            // 初始化分片。
            InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
            //文件预览
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setHeader("Content-Disposition" ,"inline");
            request.setObjectMetadata(metadata);
            // 返回uploadId。
            String uploadId = upresult.getUploadId();
            // 根据uploadId执行取消分片上传事件或者列举已上传分片的操作。
            // 如果您需要根据您需要uploadId执行取消分片上传事件的操作，您需要在调用InitiateMultipartUpload完成初始化分片之后获取uploadId。
            // 如果您需要根据您需要uploadId执行列举已上传分片的操作，您需要在调用InitiateMultipartUpload完成初始化分片之后，且在调用CompleteMultipartUpload完成分片上传之前获取uploadId。
            // System.out.println(uploadId);

            // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
            List<PartETag> partETags =  new ArrayList<PartETag>();
            // 每个分片的大小，用于计算文件有多少个分片。单位为字节。
            final long partSize = 1 * 1024 * 1024L;   //1 MB。

            // 根据上传的数据大小计算分片数。以本地文件为例，说明如何通过File.length()获取上传数据的大小。
//            final File sampleFile = new File(String.valueOf(multipartFile));
            final File sampleFile;
            try {
                sampleFile = new File("tempFile"); // 临时文件，用于保存上传的内容
                multipartFile.transferTo(sampleFile); // 将上传的文件内容保存到临时文件中
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            long fileLength = sampleFile.length();
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }
            // 遍历分片上传。

            for (int i = 0; i < partCount; i++) {
                long startPos = i * partSize;
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
                UploadPartRequest uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(ALIYUN_OSS_BUCKETNAME);
                uploadPartRequest.setKey(objectName);
                uploadPartRequest.setUploadId(uploadId);
                // 设置上传的分片流。
                // 以本地文件为例说明如何创建FIleInputstream，并通过InputStream.skip()方法跳过指定数据。
                InputStream instream = new FileInputStream(sampleFile);
                instream.skip(startPos);
                uploadPartRequest.setInputStream(instream);
                // 设置分片大小。除了最后一个分片没有大小限制，其他的分片最小为100 KB。
                uploadPartRequest.setPartSize(curPartSize);
                // 设置分片号。每一个上传的分片都有一个分片号，取值范围是1~10000，如果超出此范围，OSS将返回InvalidArgument错误码。
                uploadPartRequest.setPartNumber( i + 1);
                // 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
                UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                // 每次上传分片之后，OSS的返回结果包含PartETag。PartETag将被保存在partETags中。
                partETags.add(uploadPartResult.getPartETag());
            }

            // 创建CompleteMultipartUploadRequest对象。
            // 在执行完成分片上传操作时，需要提供所有有效的partETags。OSS收到提交的partETags后，会逐一验证每个分片的有效性。当所有的数据分片验证通过后，OSS将把这些分片组合成一个完整的文件。
            CompleteMultipartUploadRequest completeMultipartUploadRequest =
                    new CompleteMultipartUploadRequest(ALIYUN_OSS_BUCKETNAME, objectName, uploadId, partETags);
            // 如果需要在完成分片上传的同时设置文件访问权限，请参考以下示例代码。
            // completeMultipartUploadRequest.setObjectACL(CannedAccessControlList.Private);
            // 指定是否列举当前UploadId已上传的所有Part。仅在Java SDK为3.14.0及以上版本时，支持通过服务端List分片数据来合并完整文件时，将CompleteMultipartUploadRequest中的partETags设置为null。
            // Map<String, String> headers = new HashMap<String, String>();
            // 如果指定了x-oss-complete-all:yes，则OSS会列举当前UploadId已上传的所有Part，然后按照PartNumber的序号排序并执行CompleteMultipartUpload操作。
            // 如果指定了x-oss-complete-all:yes，则不允许继续指定body，否则报错。
            // headers.put("x-oss-complete-all","yes");
            // completeMultipartUploadRequest.setHeaders(headers);

            // 完成分片上传。
            CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);
            System.out.println(completeMultipartUploadResult.getETag());

            Date expiration = new Date(System.currentTimeMillis() + 10L * 30 * 24 * 3600 * 1000);

            URL signedUrl = ossClient.generatePresignedUrl(ALIYUN_OSS_BUCKETNAME, objectName, expiration);
            String path = signedUrl.getPath();
            String url = DOMAIN + path ;
            ossClient.shutdown();
            filess filess_f = new filess(url,objectName);

            return filess_f;
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return null;
    }

    public filess fileUploadZone(MultipartFile file) {
        OSS ossClient=null;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
// 获取文件名
            String fileName = file.getOriginalFilename();
// 获取文件后缀名
            assert fileName != null;
            String suffixName = fileName.substring(fileName.lastIndexOf("."));
// 最后上传生成的文件名
            String finalFileName = System.currentTimeMillis() + "" + new SecureRandom().nextInt(0x0400) + suffixName;
// oss中的文件夹名
            String objectName = "studyFile/"+ sdf.format(new Date()) + "/" + finalFileName;
// 创建上传文件的元信息，可以通过文件元信息设置HTTP header(设置了才能通过返回的链接直接访问)。
//        ObjectMetadata objectMetadata = new ObjectMetadata();
//        objectMetadata.setContentType("image/jpg");
            //文件预览
//            ObjectMetadata metadata = new ObjectMetadata();
//            metadata.setHeader("Content-Disposition", "inline");
//            metadata.setContentType(Mimetypes.getInstance().getMimetype(new File(String.valueOf(file)), objectName));

            // 创建OSSClient实例。
            ossClient = new OSSClient(ALIYUN_OSS_ENDPOINT, ALIYUN_OSS_ACCESSKEYID, ALIYUN_OSS_ACCESSKEYSECRET);
            // 创建InitiateMultipartUploadRequest对象。
            InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(ALIYUN_OSS_BUCKETNAME, objectName);
            // 如果需要在初始化分片时设置文件存储类型，请参考以下示例代码。
             ObjectMetadata metadata = new ObjectMetadata();
             metadata.setHeader("Content-Disposition" ,"inline");
             //去掉小点
            String resultString = suffixName.replace(".", "");
//                metadata.setHeader("Content-Type","application/"+resultString);
            //设置多种表头
            String contentType;
            if ("pdf".equalsIgnoreCase(resultString)) {
                contentType = "application/pdf";
            } else if ("mp4".equalsIgnoreCase(resultString)) {
                contentType = "video/mp4";
            } else if ("jpg".equalsIgnoreCase(resultString) || "jpeg".equalsIgnoreCase(resultString)) {
                contentType = "image/jpeg";
            } else if ("png".equalsIgnoreCase(resultString)) {
                contentType = "image/png";
            } else if ("gif".equalsIgnoreCase(resultString)) {
                contentType = "image/gif";
            }else if ("ogg".equalsIgnoreCase(resultString)) {
                contentType = "audio/ogg";
            }else if ("xlsx".equalsIgnoreCase(resultString)) {
                contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            } else if ("docx".equalsIgnoreCase(resultString)) {
                contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            }  else {
                // 如果是其他类型的文件，可以根据需要设置默认的 Content-Type
                contentType = "application/octet-stream";
            }
            metadata.setHeader("Content-Type", contentType);

            request.setObjectMetadata(metadata);
            // 初始化分片。
            InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
            // 返回uploadId，它是分片上传事件的唯一标识，可以根据这个uploadId发起相关的操作，如取消分片上传、查询分片上传等。
            String uploadId = upresult.getUploadId();
            // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
            List<PartETag> partETags = new ArrayList<PartETag>();
            // 计算文件有多少个分片。
            // 2MB
            final long partSize = 1 * 1024 * 1024L;
            long fileLength = file.getSize();
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }
            // 遍历分片上传。
            for (int i = 0; i < partCount; i++) {
                long startPos = i * partSize;
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
                // 跳过已经上传的分片。
                InputStream instream = file.getInputStream();
                instream.skip(startPos);
                UploadPartRequest uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(ALIYUN_OSS_BUCKETNAME);
                uploadPartRequest.setKey(objectName);
                uploadPartRequest.setUploadId(uploadId);
                uploadPartRequest.setInputStream(instream);
                // 设置分片大小。除了最后一个分片没有大小限制，其他的分片最小为100 KB。
                uploadPartRequest.setPartSize(curPartSize);
                // 设置分片号。每一个上传的分片都有一个分片号，取值范围是1~10000，如果超出这个范围，OSS将返回InvalidArgument的错误码。
                uploadPartRequest.setPartNumber(i + 1);

                // 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
                UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                // 每次上传分片之后，OSS的返回结果包含PartETag。PartETag将被保存在partETags中。
                partETags.add(uploadPartResult.getPartETag());
            }
            /**
             * 创建CompleteMultipartUploadRequest对象。
             * 在执行完成分片上传操作时，需要提供所有有效的partETags。OSS收到提交的partETags后，会逐一验证每个分片的有效性。
             * 当所有的数据分片验证通过后，OSS将把这些分片组合成一个完整的文件。
             */
            CompleteMultipartUploadRequest uploadRequest = new CompleteMultipartUploadRequest(ALIYUN_OSS_BUCKETNAME, objectName, uploadId, partETags);
            // 在完成文件上传的同时设置文件访问权限。
//            uploadRequest.setObjectACL(CannedAccessControlList.PublicRead);
            // 完成上传。
            ossClient.completeMultipartUpload(uploadRequest);
            // 关闭OSSClient。
            ossClient.shutdown();
            Date date2 = new Date();

            Date expiration = new Date(System.currentTimeMillis() + 10L * 30 * 24 * 3600 * 1000);

            URL signedUrl = ossClient.generatePresignedUrl(ALIYUN_OSS_BUCKETNAME, objectName, expiration);
            String path = signedUrl.getPath();
            String url = DOMAIN + path ;
            ossClient.shutdown();
            filess filess_f = new filess(url,objectName);
            return filess_f;
//            String endTime = date2.getHours() + ":" + date2.getMinutes() + ":" + date2.getSeconds();
//            logger.info("fileUploadZone-结束上传时间："+endTime+" 耗时："+(date2.getTime()-date.getTime())/1000);
//            Map<String, Object> map = new HashMap<>();
//            map.put("url", url);
//            map.put("name", fileName);
//            return R.ok(map);
        } catch (Exception e) {
            e.printStackTrace();
            ossClient.shutdown();
//            logger.error(e.getMessage());
            return new filess(null,null);
        }
    }

}